package repeat.personal.util;

import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;

;

/**
 * 描述:通用工具接口
 *
 * @author Zhanggq
 * @date 2019/5/13 14:47
 */
public interface PersonalTool {
    Logger logger = LoggerFactory.getLogger(PersonalTool.class);

    DateTimeFormatter HOUR_MINUTE_SECOND = DateTimeFormatter.ofPattern("HH:mm:ss");
    DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    DateTimeFormatter YYYY_MM_DD_HH_MM_SS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
    String YEAR_MONTH = "yyyyMM";
    String YEAR_MONTH_DAY = "yyyyMMdd";
    String YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyyMMddHHmmss";


    /**
     * 获取异常的详细信息.
     *
     * @param e
     * @return
     */
    static String stackTrace2String(Throwable e) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(os);
        e.printStackTrace(ps);
        return os.toString();
    }


    /**
     * 根据class创建一个对象，并从src中复制属性，注意：只有名称和类型完全相同并有相应的setter及getter方法才能复制成功.
     *
     * @param src
     * @param clazz
     * @param <T>
     * @return
     */
    static <T> T newInstance(Object src, Class<T> clazz) {
        T t = newInstance(clazz);
        copyProperties(src, t);
        return t;
    }


    /**
     * 根据class创建一个对象
     *
     * @param tClass
     * @param <T>
     * @return
     */
    static <T> T newInstance(Class<T> tClass) {
        T target = org.springframework.beans.BeanUtils.instantiate(tClass);
        return target;
    }


    /**
     * 复制属性。从src复制属性到target中。注意只有属性名和类型完全一样才能被复制。
     *
     * @param src
     * @param target
     */
    static void copyProperties(Object src, Object target) {
        org.springframework.beans.BeanUtils.copyProperties(src, target);
    }


    /**
     * 对集合进行分组
     *
     * @param collection
     * @param keyFunc
     * @param <K>
     * @param <D>
     * @return
     */
    static <K, D> Map<K, List<D>> grouping(Collection<D> collection, Function<D, K> keyFunc) {
        Map<K, List<D>> map = new HashMap<>();
        if (CollectionUtils.isEmpty(collection)) {
            return map;
        }
        for (D d : collection) {
            K k = keyFunc.apply(d);
            List<D> list = map.get(k);
            if (list == null) {
                list = new ArrayList<>();
                map.put(k, list);
            }
            list.add(d);
        }
        return map;
    }

    /**
     * 生成Map
     *
     * @param collection
     * @param keyFunc
     * @param <K>
     * @param <D>
     * @return
     */
    static <K, D> Map<K, D> mapping(Collection<D> collection, Function<D, K> keyFunc) {
        Map<K, D> map = new HashMap<>();
        if (CollectionUtils.isEmpty(collection)) {
            return map;
        }
        for (D d : collection) {
            K k = keyFunc.apply(d);
            map.put(k, d);
        }
        return map;
    }


    /**
     * 数组转List
     *
     * @param rs
     * @param <T>
     * @return
     */
    static <T> List<T> array2List(T[] rs) {
        if (rs == null || rs.length == 0) {
            return new ArrayList<>(0);
        }
        List<T> list = Lists.newArrayListWithCapacity(rs.length);
        for (T t : rs) {
            list.add(t);
        }
        return list;
    }

    /**
     * 数组转Set
     *
     * @param rs
     * @param <T>
     * @return
     */
    static <T> Set<T> array2Set(T[] rs) {
        Set<T> set = new HashSet<>();
        if (rs == null || rs.length == 0) {
            return set;
        }
        for (T t : rs) {
            set.add(t);
        }
        return set;
    }


    /**
     * List转List
     *
     * @param rs
     * @param func
     * @param <T>
     * @param <R>
     * @return
     */
    static <T, R> List<R> collection2List(Collection<T> rs, Function<T, R> func) {
        if (CollectionUtils.isEmpty(rs)) {
            return new ArrayList<>(0);
        }
        List<R> list = Lists.newArrayListWithCapacity(rs.size());
        for (T t : rs) {
            list.add(func.apply(t));
        }
        return list;
    }

    /**
     * 过滤集合，返回List
     *
     * @param rs
     * @param predicate
     * @param <T>
     * @return
     */
    static <T> List<T> filter2List(Collection<T> rs, Predicate<? super T> predicate) {
        if (CollectionUtils.isEmpty(rs)) {
            return new ArrayList<>(0);
        }
        List<T> list = Lists.newArrayListWithCapacity(rs.size());
        for (T t : rs) {
            if (predicate.test(t)) {
                list.add(t);
            }
        }
        return list;
    }

    /**
     * 过滤集合，返回Set
     *
     * @param rs
     * @param predicate
     * @param <T>
     * @return
     */
    static <T> Set<T> filter2Set(Collection<T> rs, Predicate<? super T> predicate) {
        Set<T> set = new HashSet<>();
        if (CollectionUtils.isEmpty(rs)) {
            return set;
        }
        for (T t : rs) {
            if (predicate.test(t)) {
                set.add(t);
            }
        }
        return set;
    }


    /**
     * 获取当前时间
     *
     * @return 当前时间
     */
    static String getNow() {
        StringBuilder sb = new StringBuilder();
        //当前日期
        LocalDate localDate = LocalDate.now();
        String localDateStr = localDate.format(YYYY_MM_DD);
        //当前时间
        LocalTime now = LocalTime.now();
        String localTimeStr = now.format(HOUR_MINUTE_SECOND);
        sb.append(localDateStr).append(" ").append(localTimeStr);
        return sb.toString();
    }

    /**
     * 将{@linkplain Date}转换成{@linkplain LocalDateTime}
     *
     * @param date {@linkplain Date}
     * @return {@linkplain LocalDateTime}
     */
    static LocalDateTime date2LocalDateTime(Date date) {
        if (Objects.isNull(date)){
            return null;
        }
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        return instant.atZone(zoneId).toLocalDateTime();
    }

    /**
     * 把{@linkplain LocalDateTime}转换为{@linkplain PersonalTool#YYYY_MM_DD_HH_MM_SS}格式的字符串
     *
     * @param localDateTime {@linkplain LocalDateTime} 当前时间
     * @return 时间的指定类型的字符串形式
     */
    static String localDateTime2Str(LocalDateTime localDateTime) {
        if (!Objects.isNull(localDateTime)) {
            return localDateTime.format(YYYY_MM_DD_HH_MM_SS);
        }
        return null;
    }

    /**
     * 把{@linkplain LocalDateTime}转换为指定格式的字符串
     *
     * @param localDateTime     {@linkplain LocalDateTime} 当前时间
     * @param dateTimeFormatter 时间的指定类型的字符串形式
     * @return
     */
    static String localDateTime2Str(LocalDateTime localDateTime, DateTimeFormatter dateTimeFormatter) {
        return localDateTime.format(dateTimeFormatter);
    }

    /**
     * 将{@linkplain Date}转换成{@linkplain PersonalTool#YYYY_MM_DD_HH_MM_SS}格式的字符串
     *
     * @param date {@linkplain Date}
     * @return 字符串
     */
    static String date2Str(Date date) {
        if (Objects.isNull(date)){
            return null;
        }
        LocalDateTime localDateTime = date2LocalDateTime(date);
        if (!Objects.isNull(localDateTime)) {
            return localDateTime2Str(localDateTime);
        }
        return null;
    }

    /**
     * 将{@linkplain Date}转换成指定格式的字符串
     *
     * <pre>
     *     1.如果{@linkplain Date}或者{@linkplain DateTimeFormatter}为空,调用{@linkplain PersonalTool#date2Str(Date)}返回对应的字符串
     * </pre>
     *
     * @param date              {@linkplain Date}
     * @param dateTimeFormatter {@linkplain DateTimeFormatter}
     * @return 字符串
     */
    static String date2Str(Date date, DateTimeFormatter dateTimeFormatter) {
        if (Objects.isNull(date) || Objects.isNull(dateTimeFormatter)) {
            return date2Str(date);
        }
        LocalDateTime localDateTime = date2LocalDateTime(date);
        if (!Objects.isNull(localDateTime)) {
            return localDateTime2Str(localDateTime, dateTimeFormatter);
        }
        return null;
    }

    /**
     * 将时间字符串转换成{@linkplain PersonalTool#YYYY_MM_DD_HH_MM_SS}格式的{@linkplain LocalDateTime}
     *
     * @param dateStr 日期字符串
     * @return 指定格式的{@linkplain LocalDateTime}
     */
    static LocalDateTime str2LocalDateTime(String dateStr) {
        return LocalDateTime.parse(dateStr, YYYY_MM_DD_HH_MM_SS);
    }

    /**
     * 将日期字符串转换成指定格式的{@linkplain LocalDateTime}
     *
     * @param dateStr 日期字符串
     * @return
     */
    static LocalDateTime str2LocalDateTime(String dateStr, DateTimeFormatter dateTimeFormatter) {
        return LocalDateTime.parse(dateStr, dateTimeFormatter);
    }

    /**
     * 将{@linkplain LocalDateTime}转换成{@linkplain Date}
     *
     * @param localDateTime {@linkplain LocalDateTime}
     * @return {@linkplain Date}
     */
    static Date localDateTime2Date(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDateTime.atZone(zoneId);
        return Date.from(zdt.toInstant());
    }

    /**
     * 将时间字符串转换成{@linkplain PersonalTool#YYYY_MM_DD_HH_MM_SS}格式的{@linkplain Date}
     */
    static Date str2Date(String dateStr, DateTimeFormatter dateTimeFormatter) {
        LocalDateTime localDateTime = str2LocalDateTime(dateStr, dateTimeFormatter);
        if (!Objects.isNull(localDateTime)) {
            return localDateTime2Date(localDateTime);
        }
        return null;
    }

    /**
     * 流读写文件
     * @param ins
     * @param file
     */
    static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
